EXC-ERR

Section: EXCEPTION LIBRARY (3)
Updated: 28 July 1993
Index Return to Main Contents
 

NAME

Exception - exception handing in ANSI C  

SYNOPSIS


#include <exception/exception.h>


TRY (try-block, unwind-block)
CATCH (domain, catch-form)
UNWIND_PROTECT (protected-block, cleanup-block)


continue
break
tryreturn (lvalue)


THROW (exception)
THROW_TYPED (exception, type)


void exc_throw (void *exception_addr)
void exc_throw_typed (void *exception_addr, void *exception_type)
void exc_rethrow (void)


void *exception
void *exc_exception (void)
void *exc_type (void)


int EXC_IN_DOMAIN (exception, domain)
int EXC_EQUAL (exception1, exception2)


int exc_in_domain (void *exception_addr, void *domain_addr, unsigned long domain_size)
int exc_equal (void *exception_addr1, void *exception_addr2)


typedef int (*excHandler) (void *exception_addr, void *exception_type, void *handler_data)


void EXC_INSTALL_HANDLER (domain, excHandler handler, void *handler_data)
void EXC_REMOVE_HANDLER (domain, excHandler handler, void *handler_data)


void exc_install_handler (void *domain_addr, unsigned long domain_size, excHandler handler, void *handler_data)
void exc_remove_handler (void *domain_addr, unsigned long domain_size, excHandler handler, void *handler_data)


int exc_any
EXC_ANY


typedef enum
{


excBeginCallback,
excEndCallback,
excThrowCallback
excRecoverCallback


} excCallbackTag


typedef void (*excCallback) (excCallbackTag tag, void *callback_data, void **try_data)


void exc_install_callback (excCallbackTag tags, excCallback callback, void *callback_data)
void exc_remove_callback (excCallbackTag tags, excCallback callback, void *callback_data)


int exc_sigmask


void exc_breakpoint (void)  

DESCRIPTION

 

Throwing and Catching Exceptions

This manual page describes an exception handling system for ANSI C programs, implemented with the standard setjmp(3) and longjmp(3) library functions. See [1] for details.

TRY() associates two blocks of code, the try-block and the unwind-block. If an exception is thrown with THROW() in the try-block, the control of execution will be transferred immediately to the corresponding unwind-block. The argument of THROW() is a variable whose address identifies the exception. Any lvalue can be used, but care should be taken with automatic variables, since they may go out of scope when the exception is thrown.

The unwind-block may contain one or several CATCH() macros. Each CATCH() associates a code segment, catch-form, with a specific exception or an exception domain. When an exception has been thrown, the catch-form of the first CATCH() with a matching tag (domain) will be executed. Then, execution will continue after the TRY() macro. If there are no CATCH() macros, or if none of them match the exception thrown, control will be transferred to the unwind-block of the closest surrounding TRY() (if any).

A continue statement at the outer-most level in a try-block transfers control to the point just beyond the TRY() macro. break and return are not allowed in try-blocks and will generate run-time error messages if they are used there. However, the special tryreturn() macro can return an lvalue, but not a general expression.

continue is also allowed in unwind-blocks but should only be used there when the program has recovered from the exception so that normal execution may resume. If a continue is encountered, the rest of the unwind-block will be skipped. The CATCH() macro inserts an invisible continue at the end of catch-form, but continue may still be used explicitly in a catch-form to leave the CATCH (and the TRY) prematurely.

A break in a catch-form forces execution to continue after the CATCH() in the same unwind-block. break is not allowed in an unwind-block outside CATCH(). return is not allowed in unwind-blocks at all; use tryreturn() instead. tryreturn() cannot be used outside try-blocks and unwind-blocks.

Of course, break and continue have their usual meaning inside switchwhile and do statements used in try-blocks and unwind-blocks.

THROW() is allowed in unwind-blocks, both inside and outside CATCH() macros.

exception is a local variable that is defined in unwind-blocks, and only there. It contains the current (pending) exception, i.e. the address of the exception variable that has been thrown. The current exception can be thrown again in an unwind-block with THROW(exception). (That is a special form: it is the pending exception and not the address of the local variable that is thrown. Changing the value of exception by direct assignment has no effect.)

Occasionally, it is convenient to handle exception variable addresses, instead of specifying the exception variables themselves. exc_throw() throws an exception identified by exception_addr. Thus, THROW(e) is equivalent to exc_throw(&e). Since the exception variable is not available in functions called from unwind-blocks, there is a corresponding function exc_exception() which returns the current exception. The value can be used in a subsequent exc_throw() call, such as exc_throw(exc_exception()). There is a special function for that particular combination, namely exc_rethrow(). Note that it is an error to call exc_exception() if there is no exception pending.

UNWIND_PROTECT() is similar to TRY(), but cleanup-block will always be executed, whether an exception is thrown in protected-block or not. Thus, it is similar to the unwind-protect special form in Common Lisp. However, if protected-block is exited with tryreturn() or goto(), cleanup-block will not be executed.  

Identifying Exceptions

Although CATCH() is the recommended way of identifying exceptions, EXC_IN_DOMAIN() and EXC_EQUAL() can be used in explicit tests. EXC_IN_DOMAIN() returns a non-zero value only if exception belongs to domain. EXC_EQUAL() is a more restrictive test that returns non-zero only if exception1 is identical to a particular member, exception2, of an exception domain. exc_in_domain() and exc_equal() are the corresponding functions which accept exception variable address parameters. The third argument of exc_in_domain() is the exception domain size in bytes: EXC_IN_DOMAIN(E, D) is equivalent to exc_in_domain(&E, &D, sizeof(D)).  

Exception Handlers

If an exception is thrown beyond the outer-most TRY() block, a list of exception handlers will be called. A handler must be of type excHandler and can be installed with EXC_INSTALL_HANDLER(). The handler will be called only for exceptions matching the domain argument.

There is a corresponding function, exc_install_handler(), which accepts the address of an exception domain variable. The domain_size parameter value must be sizeof(domain). As a special case, the predefined symbol exc_any can be specified as the domain argument of EXC_INSTALL_HANDLER(), in which case the handler will be called for all exceptions thrown beyond the top-level TRY(). (Specifying EXC_ANY or NULL to exc_install_handler() has the same effect.)

A handler can be removed with EXC_REMOVE_HANDLER() or exc_remove_handler(). The arguments must match the corresponding installation call exactly, or an error message will be printed.

When the handler is called, exception_addr will contain the pending exception, and exception_type will contain the corresponding type tag (see below). These two parameters can be used for catching certain exceptions or exception types, while ignoring others.

The handler_data argument is private to the handler and is not interpreted by EXC_INSTALL_HANDLER().

If the handler returns, the system will consider the pending exception to be completely processed and will call exit() with the status value returned by the handler. On the other hand, if the handler throws a new exception or calls exc_rethrow(), the next applicable handler will be called. No handler should call exit() or abort() explicitly.  

Exception Types

Since exceptions are handled through void pointers, the original C type of an exception variable is only implicit in catch-forms and exception handlers. However, an exception variable can be associated with a type, which is represented by another variable address, if the exception is thrown with THROW_TYPED() or exc_throw_typed(). The type of the pending exception is returned by exc_type() and can be checked with EXC_IN_DOMAIN(). The interpretation of exception types is entirely defined by the application code using them.  

Callbacks

A callback function of type excCallback can be installed with exc_install_callback(). All installed callbacks will be called whenever a new TRY() is entered or exited. The tag parameter of the callback will be excBeginCallback when the TRY() is entered and excEndCallback when it is exited. If an exception is thrown in the try-block, the callbacks are called with tag equal to excThrowCallback. If and when the last unwind-block has finished, the program has recovered from the exception and the callbacks will be called with an excRecoverCallback tag. However, a callback will only be called for a particular event if the corresponding bit was set in the tags argument of the EXC_INSTALL_CALLBACK() call which installed the callback.

Callbacks can be removed with exc_remove_callback(). All parameters must match the ones in the corresponding installation call, or an error message will be generated.

The callback_data parameter corresponds to handler_data for exception handlers and is private to the callback function. The try_data argument, on the other hand, will contain the address of a void pointer variable, which is allocated for each installed callback every time a TRY() is entered. The pointer variable is writable and can be used by the callback for any purpose. Usually, the callback stores information associated with the TRY() in it during the excBeginCallback call. This information can be used later on, when the function is called with excEndCallback or excThrowCallback. If a callback is installed inside a try-block, the try_data pointer will be NULL when the corresponding TRY() is exited (either normally or with a throw).  

Signals

The unwind-blocks are usually executed with all signals blocked: since signal handlers might throw exceptions there would otherwise be no guarantee that all the unwind code would actually be executed. For efficiency reasons, the current signal mask is not saved every time a TRY() is entered. Instead, the signal mask in effect at the original throw-point will be restored. This value is held in the global exc_sigmask variable during exception processing, and can be changed in an unwind-block. See [1] and exc-sig(3) for details.  

Debugging

When programs using the exception(3) library are being debugged, it is often desirable to interrupt execution when an exception is thrown, and before the stack has been unwound. The dummy function exc_breakpoint() is provided for that purpose. If will be called before any installed callbacks.

Note that an exception does not necessarily indicate an error. For example, the UNWIND_PROTECT() macro does always throw an exception. However, most libraries provide a special error function that calls exc_throw(). It is sometimes more convenient to set a breakpoint in such a function than in exc_breakpoint().  

EXAMPLES


static int out_of_memory;
volatile int i;
char * volatile p[10];


TRY (
{


while (i<10)
{

p[i] = malloc (1024);
if (p[i] == NULL)

THROW (out_of_memory);

i++;

}

},
{

CATCH (out_of_memory,
{

while (i-- > 0)

free (p[i]);

});

});  

FILES


$CVAPHOME/include/exception/exception.h
$CVAPHOME/lib/libexception.a
$CVAPHOME/lib/libexception-dbx.a  

SEE ALSO

exc-err(3), exc-sig(3)

[1] H. Winroth, Exception Handling in ANSI C, CVAP Tech. Rep., March 1993.  

AUTHORS

Harald Winroth (harald@bion.kth.se), CVAP, NADA, KTH.
Matti Rendahl (matti@bion.kth.se), CVAP, NADA, KTH.


 

Index

NAME
SYNOPSIS
DESCRIPTION
Throwing and Catching Exceptions
Identifying Exceptions
Exception Handlers
Exception Types
Callbacks
Signals
Debugging
EXAMPLES
FILES
SEE ALSO
AUTHORS

This document was created by man2html, using the manual pages.
Time: 05:25:06 GMT, September 03, 2025